/*
 * Copyright (C), 2002-2013, 苏宁易购电子商务有限公司
 * FileName: ValidationUtil.java
 * Author:   12070619@cnsuning.com
 * Date:     Oct 24, 2013 10:52:27 AM
 * Description: //模块目的、功能描述      
 * History: //修改记录
 * <author>      <time>      <version>    <desc>
 * 修改人姓名             修改时间            版本号                  描述
 */
package com.ws.framework.util;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.springframework.util.StringUtils;

/**
 * RSA安全编码组件
 * 
 * @author WSH
 */

public class ValidationUtil {
    public static final String ENCODER_UTF8 = "UTF-8";
    public static final String ENCODER_ISO8859 = "ISO-8859-1";
    public static final String KEY_MAC = "HmacMD5";
    public static final long TIMESTAMP_INTERVAL = 5 * 60 * 1000;// 五分钟

    public static void validate(Map<String, Object> paramMap, String key, String mac) {
        if (paramMap == null || paramMap.size() <= 1 || !StringUtils.hasText(key) || !StringUtils.hasText(mac)) {
            throw new RuntimeException("invalid request!");
        }
        try {
            long timeStamp = (Long) paramMap.get("timeStamp");
            if (System.currentTimeMillis() - timeStamp > TIMESTAMP_INTERVAL) {
                throw new RuntimeException("invalid request!");
            }
            String paramStr = getParamFromMap(paramMap);
            String newMac = encryptHMAC(paramStr, key);
            if (!mac.equals(newMac)) {
                throw new RuntimeException("invalid request!");
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * 功能描述: <br>
     * 数据加密处理
     *
     * @param data 待加密数据
     * @param key 加密密钥
     * @return
     * @throws Exception
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private static String encryptHMAC(String data, String key) throws Exception {
        String keyStr = URLDecoder.decode(key, ENCODER_UTF8);
        byte[] keyRaw = keyStr.getBytes(ENCODER_ISO8859);
        SecretKey secretKey = new SecretKeySpec(keyRaw, KEY_MAC);
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());
        mac.init(secretKey);
        byte[] raw = mac.doFinal(data.getBytes(ENCODER_UTF8));
        return new String(raw, ENCODER_ISO8859);
    }

    /**
     * 功能描述: <br>
     * 获取私钥信息
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static String getHmacKey() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);
        SecretKey secretKey = keyGenerator.generateKey();
        String keyStr = new String(secretKey.getEncoded(), ENCODER_ISO8859);
        return URLEncoder.encode(keyStr, ENCODER_UTF8);
    }

    /**
     * 功能描述: <br>
     * 从消息参数/私钥map中获取参数
     *
     * @param map 消息参数/私钥 map
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    @SuppressWarnings("rawtypes")
    private static String getParamFromMap(Map<String, Object> map) {
        if (map == null) {
            throw new IllegalArgumentException("map is null!");
        }
        List<Map.Entry<String, Object>> mappingList = new ArrayList<Map.Entry<String, Object>>(map.entrySet());
        Collections.sort(mappingList, new Comparator<Map.Entry<String, Object>>() {
            public int compare(Map.Entry<String, Object> entry1, Map.Entry<String, Object> entry2) {
                return entry1.getKey().compareTo(entry2.getKey());
            }
        });
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, Object> entry : mappingList) {
            Object obj = entry.getValue();
            if (BASEDATATYPE.contains(obj.getClass())) { // String类型
                builder.append(entry.getKey()).append("=").append(obj).append("&");
            } else if (obj.getClass().isArray()) {
                int arrLength = Array.getLength(obj);
                if (arrLength > 0) {
                    for (int i = 0; i < arrLength; i++) {
                        builder.append(entry.getKey()).append("=").append(Array.get(obj, i)).append("&");
                    }
                }
            } else if (obj instanceof Collection) {
                Collection collection = (Collection) obj;
                if (collection.size() > 0) {
                    for (Object o : collection) {
                        builder.append(entry.getKey()).append("=").append(o).append("&");
                    }
                }
            } else {
                throw new RuntimeException("Can't parse complex Object:" + obj.getClass());
            }
        }
        builder.deleteCharAt(builder.lastIndexOf("&"));
        return builder.toString();
    }

    @SuppressWarnings({ "rawtypes", "serial" })
    private static final Set<Class> BASEDATATYPE = new HashSet<Class>() {
        {
            add(short.class);
            add(int.class);
            add(long.class);
            add(float.class);
            add(double.class);
            add(boolean.class);
            add(char.class);
            add(Short.class);
            add(Integer.class);
            add(Long.class);
            add(Float.class);
            add(Double.class);
            add(Boolean.class);
            add(Character.class);
            add(String.class);
        }
    };
}
